#include "stdafx.h"
#include "ServerConnection.h"
#include "XMLConnection.h"
#include "CoreXMLHandlerManager.h"
#include "Packages.h"
#include "TempConnection.h"
#include "Logger.h"
#include "SafeMutex.h"

#include "Poco\Net\SocketStream.h"
#include "Poco\SAX\InputSource.h"
#include "Poco\Exception.h"
#include "Poco\Logger.h"
#include "Poco\BasicEvent.h"
#include "Poco\Delegate.h"
#include "Poco\Format.h"

namespace ExamSystem
{
    using Poco::Net::SocketStream;
    using Poco::XML::InputSource;
    using Poco::Exception;
    using Poco::Logger;
    using Poco::Message;
    using Poco::Delegate;

    CExamServerConnectionManager::CExamServerConnectionManager(void)
        : m_threadPool("ServerThreadPool", 2, MaxConnectedClient)
        , m_xmlHandlerManager(new CCoreXMLHandlerManager)
        , m_port(12321)
        , m_keepAliveTimer(0, 0)
        , m_keepAliveTimerCount(0)
        , m_keepAliveTimerCallback(*this, &CExamServerConnectionManager::OnKeepAliveTimer)
        , m_flagServerRunning(false)
    {
        m_keepAliveTimer.setPeriodicInterval(5000);
        m_keepAliveTimerCount = 0;
        m_keepAliveTimer.start(m_keepAliveTimerCallback);
    }

    CExamServerConnectionManager::~CExamServerConnectionManager(void)
    {

    }

    void CExamServerConnectionManager::ManageConnection(const StreamSocket &s)
    {
        {
            CSafeScopedLock scopeLock(m_lockServerRunningFlag);
            if (!m_flagServerRunning)
                return;
        }

        size_t i = 0;
        SharedPtr<CXMLConnection> pXMLConnection;
        {
            CSafeScopedLock scopeLock2(m_lockConnectionManager);
            for (; i < m_managedConnections.size(); i++)
            {
                if (NULL == m_managedConnections[i])
                {
                    break;
                }
            }

            if (i == m_managedConnections.size())
                m_managedConnections.push_back(NULL);

            pXMLConnection = new CXMLConnection(s, m_xmlHandlerManager);
            pXMLConnection->SetLocalID(i);

            m_managedConnections[i] = pXMLConnection;
        }

        pXMLConnection->eventConnectionClosed +=
            Delegate<CExamServerConnectionManager, int>(this, &CExamServerConnectionManager::OnConnectionClosed);
        
        m_threadPool.start(*pXMLConnection);

        char buf[128];
        sprintf_s(buf, "Client %u connected." , i);
        systemLogger.information(buf);
    }

    void CExamServerConnectionManager::StartServer(void)
    {
        CSafeScopedLock scope(m_lockServerRunningFlag);
        if (m_flagServerRunning)
            return;

        // create a server socket and listen
        m_serverSocket = new ServerSocket(m_port);
        m_tcpServer = new TCPServer(new CTempConnectionFactory(this), *m_serverSocket);
        m_tcpServer->start();

        m_flagServerRunning = true;

        systemLogger.information(Poco::format("Start listening on port: %u", m_port));
    }

    void CExamServerConnectionManager::StopServer(void)
    {
        CSafeScopedLock scopeLock(m_lockServerRunningFlag);
        if (!m_flagServerRunning)
            return;

        if (NULL != m_tcpServer)
            m_tcpServer->stop();

        m_keepAliveTimer.stop();
        // close all connected connections
        std::vector<SharedPtr<CXMLConnection> > connections;

        {
            CSafeScopedLock scopeLock2(m_lockConnectionManager);
            connections = m_managedConnections;
        }

        for (size_t i = 0; i < connections.size(); i++)
            if (NULL != connections[i])
                connections[i]->CloseConnection();

        m_threadPool.joinAll();
        m_flagServerRunning = false;
    }

    SharedPtr<CCoreXMLHandlerManager> & CExamServerConnectionManager::GetHandlerManager(void)
    {
        return m_xmlHandlerManager;
    }

    const SharedPtr<CCoreXMLHandlerManager> & CExamServerConnectionManager::GetHandlerManager(void) const
    {
        return m_xmlHandlerManager;
    }

    void CExamServerConnectionManager::SetPort(unsigned int port)
    {
        m_port = port;
    }

    void CExamServerConnectionManager::OnConnectionClosed(const void* pSender, int &id)
    {
        CSafeScopedLock scope(m_lockConnectionManager);

        char buf[128];
        //int lid = id;
        sprintf_s(buf, "Client %d disconnected.", id);
        systemLogger.information(buf);

        eventConnectionClosed(this, id);
        // wait the dead connection finished
        if (m_closedConnection)
            m_closedConnection->WaitForConnectionStop();

        m_closedConnection = m_managedConnections[id];
        m_managedConnections[id] = NULL;
    }

    void CExamServerConnectionManager::OnKeepAliveTimer(Timer &timer)
    {
        switch (m_keepAliveTimerCount)
        {
        case 0: // send
            SendKeepAlivePackages();
            break;
        case 1: // check
            CheckKeepAliveReplies();
            break;
        }
        m_keepAliveTimerCount = (m_keepAliveTimerCount + 1) % 3;
    }

    void CExamServerConnectionManager::SendKeepAlivePackages(void)
    {
        CSafeScopedLock scope(m_lockConnectionManager);

        for (size_t i = 0; i < m_managedConnections.size(); i++)
        {
            if (NULL == m_managedConnections[i])
                continue;
            
            m_managedConnections[i]->SendKeepAlivePackage();
        }

    }

    void CExamServerConnectionManager::CheckKeepAliveReplies(void)
    {
        // we don't check the reply here, for current strategy can't deal with
        // the situation that the connection is sending or receiving large data
        // blocks.
        // 
        // TODO: add a more reliable checking strategy
        // 
        // 
        //CSafeScopedLock scope(m_lockConnectionManager);

        //for (size_t i = 0; i < m_managedConnections.size(); i++)
        //{
        //    if (NULL == m_managedConnections[i])
        //        continue;

        //    if (!m_managedConnections[i]->IsKeepAlivePackageReplied())
        //        m_managedConnections[i]->CloseConnection();
        //}
    }

    const SharedPtr<CXMLConnection> &CExamServerConnectionManager::GetXMLConnection(int id)
    {
        return m_managedConnections[id];
    }

    std::string CExamServerConnectionManager::GetListenAddress(void) const
    {
        if (m_serverSocket)
            return m_serverSocket->address().toString();
        else
            return "";
    }



}